#include "WakeupChannel.h"
#include <cstdio>
#include <utility>
#include "EventLoop.h"
#include "PlatLogger.h"
#include "Poller.h"
#include "XnccEventfd.h"
#include "XnccType.h"

namespace xncc {
namespace foundation {

    WakeupChannel::WakeupChannel(EventLoop* loop, std::unique_ptr<xncc::sys::EventFd> wakeupFd)
        : Channel(loop, wakeupFd->Fd()), wakeupFd_(std::move(wakeupFd))
    {
        TRACE_INFO << "create WakeupChannel bind loop id:" << loop->id() << " fd:" << fd()
                   << " loop type:" << types::IO_MULTI_MODE::toString(loop->mode());
    }

    WakeupChannel::WakeupChannel(EventLoop* loop) : Channel(loop, NOBLOCKPOLLER_WAKEUP_FD)
    {
        TRACE_INFO << "create WakeupChannel bind loop id:" << loop->id() << " fd:" << fd()
                   << " loop type:" << types::IO_MULTI_MODE::toString(loop->mode());
    }

    void WakeupChannel::addPendingFuncs(types::Task func)
    {
        {
            std::lock_guard<std::mutex> lock(mutex_);
            pendingFunctors_.push_back(std::move(func));
        }
        if (loopType() != types::IO_MULTI_MODE::value::NO_IO_MULTI) {
            wakeup();
        }
    }

    void WakeupChannel::handleRead()
    {
        if (readfd()) {
            doPendingFuncs();
        }
    }

    bool WakeupChannel::readfd()
    {
        uint64_t one   = 1;
        auto     nrRet = wakeupFd_->read(one);
        if (nrRet != sizeof one) {
            if (loopType() != types::IO_MULTI_MODE::value::NO_IO_MULTI) {
                SYSTEM_ERROR << ("wakeup::read failed"_s);
            }
            return false;
        }
        return true;
    }

    void WakeupChannel::doPendingFuncs()
    {
        std::vector<types::Task> functors;
        callingPendingFunctors_ = true;
        {
            std::lock_guard<std::mutex> lock(mutex_);
            functors.swap(pendingFunctors_);
        }
        for (const auto& functor : functors) {
            functor();  // 万一执行的回调是又推一个新的回调进入线程中  callingPendingFunctors_ 就起作用了
        }
        callingPendingFunctors_ = false;
    }

    void WakeupChannel::wakeup()
    {
        uint64_t one   = 1;
        ssize_t  nwRet = wakeupFd_->write(one);
        if (nwRet != sizeof one) {
            SYSTEM_ERROR << ("wakeup::write failed"_s);
        }
    }
}  // namespace foundation
}  // namespace xncc